import {
  ApiMsgEnum,
  EntityTypeEnum,
  IClientInput,
  IMsgClientSync,
  IState,
  InputTypeEnum,
} from "../Common";
import { Connection } from "../Core/Connection";
import { Player } from "./Player";
import { PlayerManager } from "./PlayerMananger";
import { RoomManager } from "./RoomManager";

export class Room {
  id: number;
  players: Set<Player> = new Set<Player>();
  pendingInput: IClientInput[] = [];

  lastTime: number;

  playerMapFrame: Map<number, number> = new Map<number, number>();

  constructor(rid: number) {
    this.id = rid;
  }

  join(uid: number) {
    let player = PlayerManager.Instance.idMapPlayer.get(uid);

    if (player) {
      player.rid = this.id;
      this.players.add(player);
    }
  }

  leave(uid: number) {
    let player = PlayerManager.Instance.idMapPlayer.get(uid);

    if (player) {
      player.rid = null;
      this.players.delete(player);

      if (!this.players.size) {
        RoomManager.Instance.closeRoom(this.id);
      }
    }
  }

  start() {
    const state: IState = {
      actors: [...this.players].map((player, index) => {
        return {
          id: player.id,
          nickname: player.nickname,
          hp: 100,
          type: EntityTypeEnum.Actor1,
          weaponType: EntityTypeEnum.Weapon1,
          bulletType: EntityTypeEnum.Bullet2,
          position: {
            x: -150 + 300 * index,
            y: -150 + 300 * index,
          },
          direction: {
            x: 0,
            y: 1,
          },
        };
      }),
      bullets: [],
      nextBulletId: 0,
      seed: 1,
    };

    for (const player of this.players) {
      player.connection.sendMsg(ApiMsgEnum.MsgGameStart, { state: state });

      player.connection.listenMsg(
        ApiMsgEnum.MsgClientSync,
        this.getClientSync,
        this
      );
    }

    const timer1 = setInterval(() => {
      this.sendServerSync();
    }, 100);

    const timer2 = setInterval(() => {
      this.timePast();
    }, 16);
  }

  timePast() {
    const now = process.uptime();
    const dt = now - (this.lastTime ?? now);
    this.lastTime = now;
    this.pendingInput.push({
      type: InputTypeEnum.TimePast,
      deltaTime: dt,
    });

    // DataManager.Instance.applyInput({
    //   type: InputTypeEnum.TimePast,
    //   deltaTime: deltaTime,
    // });
  }

  getClientSync(connection: Connection, { input, frameId }: IMsgClientSync) {
    this.pendingInput.push(input);

    this.playerMapFrame.set(connection.playerId, frameId);
  }

  sendServerSync() {
    if (this.pendingInput.length <= 0) {
      return;
    }

    const inputs = this.pendingInput;
    this.pendingInput = [];

    for (const player of this.players) {
      player.connection.sendMsg(ApiMsgEnum.MsgServerSync, {
        lastFrameId: this.playerMapFrame.get(player.id) ?? 0,
        inputs,
      });
    }
  }

  close() {}

  sync() {
    for (const player of this.players) {
      player.connection.sendMsg(ApiMsgEnum.MsgRoom, {
        room: RoomManager.Instance.getRoomView(this),
      });
    }
  }
}
